package com.bytedance.speech.speechdemo.volcengine.tts

import android.content.Context
import android.content.Intent
import android.media.AudioManager
import android.media.AudioManager.OnAudioFocusChangeListener
import android.os.Environment
import android.util.Log
import androidx.core.content.ContextCompat
import com.bytedance.speech.speechdemo.ForegroundService
import com.bytedance.speech.speechdemo.R
import com.bytedance.speech.speechdemo.SettingsActivity
import com.bytedance.speech.speechdemo.settings.Settings
import com.bytedance.speech.speechdemo.utils.SensitiveDefines
import com.bytedance.speech.speechdemo.utils.SpeechDemoDefines
import com.bytedance.speech.speechengine.SpeechEngine
import com.bytedance.speech.speechengine.SpeechEngine.SpeechListener
import com.bytedance.speech.speechengine.SpeechEngineDefines
import com.bytedance.speech.speechengine.SpeechEngineGenerator
import com.bytedance.speech.speechengine.SpeechResourceManager.FetchResourceListener
import com.bytedance.speech.speechengine.SpeechResourceManagerGenerator
import org.json.JSONException
import org.json.JSONObject
import java.io.File
import java.util.Arrays

class TTsEngine : SpeechListener {
    
    val TAG = "TTsEngine"

    private val mTtsTextTypeArray = arrayOf(
        SpeechEngineDefines.TTS_TEXT_TYPE_PLAIN,
        SpeechEngineDefines.TTS_TEXT_TYPE_SSML
    )
    private val mTtsWorkModeArray = intArrayOf(
        SpeechEngineDefines.TTS_WORK_MODE_ONLINE,
        SpeechEngineDefines.TTS_WORK_MODE_OFFLINE,
        SpeechEngineDefines.TTS_WORK_MODE_BOTH,
        SpeechEngineDefines.TTS_WORK_MODE_ALTERNATE,
        SpeechEngineDefines.TTS_WORK_MODE_FILE
    )
    private val mAuthenticationTypeArray = arrayOf(
        SpeechEngineDefines.AUTHENTICATE_TYPE_PRE_BIND,
        SpeechEngineDefines.AUTHENTICATE_TYPE_LATE_BIND
    )

    // Settings
    protected var mSettings: Settings? = null

    // Engine
    private var mSpeechEngine: SpeechEngine? = null


    // Engine State
    private var mEngineInited = false
    private var mConnectionCreated = false
    private var mEngineStarted = false
    private var mPlayerPaused = false

    // Paths
    private var mDebugPath = ""

    // Options Default Value
    private var mCurAppId = SensitiveDefines.APPID
    private var mCurTtsText = ""
    private var mCurVoiceOnline = SensitiveDefines.TTS_DEFAULT_ONLINE_VOICE
    private var mCurVoiceOffline = SensitiveDefines.TTS_DEFAULT_OFFLINE_VOICE
    private var mCurVoiceTypeOnline = SensitiveDefines.TTS_DEFAULT_ONLINE_VOICE_TYPE
    private var mCurVoiceTypeOffline = SensitiveDefines.TTS_DEFAULT_OFFLINE_VOICE_TYPE
    private var mCurTtsWorkMode = SpeechEngineDefines.TTS_WORK_MODE_ONLINE
    private var mTtsSilenceDuration = 0
    private var mTtsSpeakSpeed = 10
    private var mTtsAudioVolume = 10
    private var mTtsAudioPitch = 10

    // Android Audio Manager
    private var mAFChangeListener: OnAudioFocusChangeListener? = null
    private var mAudioManager: AudioManager? = null
    private var mResumeOnFocusGain = true
    private var mPlaybackNowAuthorized = false


    lateinit var context: Context
    fun start(context: Context) {
        this.context = context
        mAFChangeListener = OnAudioFocusChangeListener { focusChange ->
            when (focusChange) {
                AudioManager.AUDIOFOCUS_GAIN -> {
                    Log.d(
                        TAG,
                        "onAudioFocusChange: AUDIOFOCUS_GAIN, $mResumeOnFocusGain"
                    )
                    if (mResumeOnFocusGain) {
                        mResumeOnFocusGain = false
                        resumePlayback()
                    }
                }

                AudioManager.AUDIOFOCUS_LOSS -> {
                    Log.d(TAG, "onAudioFocusChange: AUDIOFOCUS_LOSS")
                    mResumeOnFocusGain = false
                    pausePlayback()
                    mPlaybackNowAuthorized = false
                }

                AudioManager.AUDIOFOCUS_LOSS_TRANSIENT -> {
                    Log.d(TAG, "onAudioFocusChange: AUDIOFOCUS_LOSS_TRANSIENT")
                    mResumeOnFocusGain = mEngineStarted
                    pausePlayback()
                }
            }
        }
        mAudioManager = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager

        val serviceIntent = Intent(context, ForegroundService::class.java)
        serviceIntent.putExtra("inputExtra", "Foreground Service Example in Android")
        ContextCompat.startForegroundService(context, serviceIntent)

        if (mDebugPath.isEmpty()) {
            mDebugPath = getDebugPath()
        }

        initEngine()

    }

    /**
     * get default debug path
     * @return string: debugPath
     */
    fun getDebugPath(): String {
        if (mDebugPath != null) {
            return mDebugPath
        }
        val state = Environment.getExternalStorageState()
        if (Environment.MEDIA_MOUNTED == state) {
            Log.d(TAG, "External storage can be read and write.")
        } else {
            Log.e(TAG, "External storage can't write.")
            return ""
        }
        val debugDir: File = context.getExternalFilesDir(null) ?: return ""
        if (!debugDir.exists()) {
            if (debugDir.mkdirs()) {
                Log.d(TAG, "Create debug path successfully.")
            } else {
                Log.e(TAG, "Failed to create debug path.")
                return ""
            }
        }
        mDebugPath = debugDir.absolutePath
        return mDebugPath
    }
    private fun resumePlayback() {
        Log.i(TAG, "继续播放")
        Log.i(TAG, "Directive: DIRECTIVE_RESUME_PLAYER")
        val ret = mSpeechEngine!!.sendDirective(SpeechEngineDefines.DIRECTIVE_RESUME_PLAYER, "")
        if (ret == SpeechEngineDefines.ERR_NO_ERROR) {
            mPlayerPaused = false
        }
        Log.d(TAG, "Resume playback status:$ret")
    }


    private fun pausePlayback() {
        Log.i(TAG, "暂停播放")
        Log.i(TAG, "Directive: DIRECTIVE_PAUSE_PLAYER")
        val ret = mSpeechEngine!!.sendDirective(SpeechEngineDefines.DIRECTIVE_PAUSE_PLAYER, "")
        if (ret == SpeechEngineDefines.ERR_NO_ERROR) {
            mPlayerPaused = true
        }
        Log.d(TAG, "Pause playback status:$ret")
    }



    private fun initEngine() {
        val viewId = SpeechDemoDefines.TTS_VIEW
        mSettings = SettingsActivity.getSettings(viewId)
        mCurTtsWorkMode =
            mTtsWorkModeArray.get(mSettings!!.getOptions(R.string.tts_work_mode_title).chooseIdx)
        Log.i(
            TAG,
            "调用初始化接口前的语音合成工作模式为 $mCurTtsWorkMode"
        )
        if (mCurTtsWorkMode == SpeechEngineDefines.TTS_WORK_MODE_ONLINE || mCurTtsWorkMode == SpeechEngineDefines.TTS_WORK_MODE_FILE) {
            // 当使用纯在线模式时，不需要下载离线合成所需资源
            initEngineInternal()
        } else {
            // 下载离线合成所需资源需要区分多音色资源和单音色资源，下载这两种资源所调用的方法略有不同
            if (mSettings!!.getOptionsValue(
                    R.string.tts_offline_resource_format_title,
                    context
                ) == "MultipleVoice"
            ) {
                // 多音色资源是指一个资源文件中包含了多个离线音色，这种资源一般是旧版(V2)离线合成所用资源
                Log.i(TAG, "当前所用资源类别为多音色资源，开始准备多音色资源")
                prepareMultipleVoiceResource()
            } else if (mSettings!!.getOptionsValue(
                    R.string.tts_offline_resource_format_title,
                    context
                ) == "SingleVoice"
            ) {
                // 单音色资源是指一个资源文件仅包含一个离线音色，新版(V4 及以上)离线合成用的就是单音色资源
                Log.i(TAG, "当前所用资源类别为单音色资源，开始准备单音色资源")
                prepareSingleVoiceResource()
            }
        }
    }

    private fun initEngineInternal() {
        var ret = SpeechEngineDefines.ERR_NO_ERROR
        if (mSpeechEngine == null) {
            Log.i(TAG, "创建引擎.")
            mSpeechEngine = SpeechEngineGenerator.getInstance()
            mSpeechEngine!!.createEngine()
        }
        if (ret != SpeechEngineDefines.ERR_NO_ERROR) {
            speechEngineInitFailed("Create engine failed: $ret")
            return
        }
        Log.d(TAG, "SDK 版本号: " + mSpeechEngine!!.version)
        Log.i(TAG, "配置初始化参数.")
        configInitParams()
        val startInitTimestamp = System.currentTimeMillis()
        Log.i(TAG, "引擎初始化.")
        ret = mSpeechEngine!!.initEngine()
        if (ret != SpeechEngineDefines.ERR_NO_ERROR) {
            val errMessage = "初始化失败，返回值: $ret"
            Log.e(TAG, errMessage)
            speechEngineInitFailed(errMessage)
            return
        }
        Log.i(TAG, "设置消息监听")
        mSpeechEngine!!.setListener(this)
        val cost = System.currentTimeMillis() - startInitTimestamp
        Log.d(TAG, String.format("初始化耗时 %d 毫秒", cost))
        speechEnginInitSucceeded(cost)
    }

   fun startEngineBtnClicked(content:String) {
        Log.d(TAG, "Start engine, current status: $mEngineStarted")
        if (!mEngineStarted) {
            AcquireAudioFocus()
            if (!mPlaybackNowAuthorized) {
                Log.w(TAG, "Acquire audio focus failed, can't play audio")
                return
            }
            clearResultText()

            // Directive：启动引擎前调用SYNC_STOP指令，保证前一次请求结束。
            Log.i(TAG, "关闭引擎（同步）")
            Log.i(TAG, "Directive: DIRECTIVE_SYNC_STOP_ENGINE")
            var ret =
                mSpeechEngine!!.sendDirective(SpeechEngineDefines.DIRECTIVE_SYNC_STOP_ENGINE, "")
            if (ret != SpeechEngineDefines.ERR_NO_ERROR) {
                Log.e(TAG, "send directive syncstop failed, $ret")
            } else {
                configStartTtsParams(content)
                Log.i(TAG, "启动引擎")
                Log.i(TAG, "Directive: DIRECTIVE_START_ENGINE")
                ret = mSpeechEngine!!.sendDirective(SpeechEngineDefines.DIRECTIVE_START_ENGINE, "")
                if (ret != SpeechEngineDefines.ERR_NO_ERROR) {
                    Log.e(TAG, "send directive start failed, $ret")
                }
            }
        }
    }

    private fun configStartTtsParams(content:String) {
        //【必需配置】TTS 使用场景
        mSpeechEngine!!.setOptionString(
            SpeechEngineDefines.PARAMS_KEY_TTS_SCENARIO_STRING,
            SpeechEngineDefines.TTS_SCENARIO_TYPE_NORMAL
        )
        val ttsText: String = content
        mCurTtsText = if (!ttsText.isEmpty()) {
            ttsText
        } else {
            "愿中国青年都摆脱冷气，只是向上走，不必听自暴自弃者流的话。能做事的做事，能发声的发声。有一分热，发一分光。就令萤火一般，也可以在黑暗里发一点光，不必等候炬火。此后如竟没有炬火：我便是唯一的光。"
        }
        //【必需配置】需合成的文本，不可超过 80 字
        mSpeechEngine!!.setOptionString(SpeechEngineDefines.PARAMS_KEY_TTS_TEXT_STRING, mCurTtsText)
        //【可选配置】需合成的文本的类型，支持直接传文本(TTS_TEXT_TYPE_PLAIN)和传 SSML 形式(TTS_TEXT_TYPE_SSML)的文本
        mSpeechEngine!!.setOptionString(
            SpeechEngineDefines.PARAMS_KEY_TTS_TEXT_TYPE_STRING,
            mTtsTextTypeArray[mSettings!!.getOptions(R.string.tts_text_type_title).chooseIdx]
        )
        mTtsSpeakSpeed = mSettings!!.getInt(R.string.config_tts_speak_speed)
        //【可选配置】用于控制 TTS 音频的语速，支持的配置范围参考火山官网 语音技术/语音合成/离在线语音合成SDK/参数说明 文档
        mSpeechEngine!!.setOptionInt(SpeechEngineDefines.PARAMS_KEY_TTS_SPEED_INT, mTtsSpeakSpeed)
        mTtsAudioVolume = mSettings!!.getInt(R.string.config_tts_audio_volume)
        //【可选配置】用于控制 TTS 音频的音量，支持的配置范围参考火山官网 语音技术/语音合成/离在线语音合成SDK/参数说明 文档
        mSpeechEngine!!.setOptionInt(SpeechEngineDefines.PARAMS_KEY_TTS_VOLUME_INT, mTtsAudioVolume)
        mTtsAudioPitch = mSettings!!.getInt(R.string.config_tts_audio_pitch)
        //【可选配置】用于控制 TTS 音频的音高，支持的配置范围参考火山官网 语音技术/语音合成/离在线语音合成SDK/参数说明 文档
        mSpeechEngine!!.setOptionInt(SpeechEngineDefines.PARAMS_KEY_TTS_PITCH_INT, mTtsAudioPitch)
        mTtsSilenceDuration = mSettings!!.getInt(R.string.config_tts_silence_duration)
        //【可选配置】是否在文本的每句结尾处添加静音段，单位：毫秒，默认为 0ms
        mSpeechEngine!!.setOptionInt(
            SpeechEngineDefines.PARAMS_KEY_TTS_SILENCE_DURATION_INT,
            mTtsSilenceDuration
        )

        //【可选配置】是否使用 SDK 内置播放器播放合成出的音频，默认为 true
        mSpeechEngine!!.setOptionBoolean(
            SpeechEngineDefines.PARAMS_KEY_TTS_ENABLE_PLAYER_BOOL,
            mSettings!!.getBoolean(R.string.config_tts_player)
        )
        //【可选配置】是否令 SDK 通过回调返回合成的音频数据，默认不返回。
        // 开启后，SDK 会流式返回音频，收到 MESSAGE_TYPE_TTS_AUDIO_DATA_END 回调表示当次合成所有的音频已经全部返回
        mSpeechEngine!!.setOptionInt(
            SpeechEngineDefines.PARAMS_KEY_TTS_DATA_CALLBACK_MODE_INT,
            if (mSettings!!.getBoolean(R.string.config_tts_data_callback)) 2 else 0
        )

        // ------------------------ 在线合成相关配置 -----------------------
        var curVoiceOnline = mSettings!!.getString(R.string.config_voice_online)
        if (curVoiceOnline.isEmpty()) {
            curVoiceOnline = mSettings!!.getOptionsValue(R.string.config_voice_online)
        }
        mCurVoiceOnline = curVoiceOnline
        Log.d(TAG, "Current online voice: $mCurVoiceOnline")
        //【必需配置】在线合成使用的发音人代号
        mSpeechEngine!!.setOptionString(
            SpeechEngineDefines.PARAMS_KEY_TTS_VOICE_ONLINE_STRING,
            mCurVoiceOnline
        )
        var curVoiceTypeOnline = mSettings!!.getString(R.string.config_voice_type_online)
        if (curVoiceTypeOnline.isEmpty()) {
            curVoiceTypeOnline = mSettings!!.getOptionsValue(R.string.config_voice_type_online)
        }
        mCurVoiceTypeOnline = curVoiceTypeOnline
        Log.d(TAG, "Current online voice type: $mCurVoiceTypeOnline")
        //【必需配置】在线合成使用的音色代号
        mSpeechEngine!!.setOptionString(
            SpeechEngineDefines.PARAMS_KEY_TTS_VOICE_TYPE_ONLINE_STRING,
            mCurVoiceTypeOnline
        )

        //【可选配置】是否打开在线合成的服务端缓存，默认关闭
        mSpeechEngine!!.setOptionBoolean(
            SpeechEngineDefines.PARAMS_KEY_TTS_ENABLE_CACHE_BOOL,
            mSettings!!.getBoolean(R.string.enable_cache)
        )
        //【可选配置】指定在线合成的语种，默认为空，即不指定
        mSpeechEngine!!.setOptionString(
            SpeechEngineDefines.PARAMS_KEY_TTS_LANGUAGE_ONLINE_STRING,
            mSettings!!.getString(R.string.config_tts_language_online)
        )
        //【可选配置】是否启用在线合成的情感预测功能
        mSpeechEngine!!.setOptionBoolean(
            SpeechEngineDefines.PARAMS_KEY_TTS_WITH_INTENT_BOOL,
            mSettings!!.getBoolean(R.string.config_tts_with_intent)
        )
        //【可选配置】指定在线合成的情感，例如 happy, sad 等
        mSpeechEngine!!.setOptionString(
            SpeechEngineDefines.PARAMS_KEY_TTS_EMOTION_STRING,
            mSettings!!.getString(R.string.config_tts_emotion)
        )
        //【可选配置】需要返回详细的播放进度时应配置为 1, 否则配置为 0 或不配置
        mSpeechEngine!!.setOptionInt(SpeechEngineDefines.PARAMS_KEY_TTS_WITH_FRONTEND_INT, 1)
        //【可选配置】使用复刻音色
        mSpeechEngine!!.setOptionBoolean(
            SpeechEngineDefines.PARAMS_KEY_TTS_USE_VOICECLONE_BOOL,
            mSettings!!.getBoolean(R.string.config_tts_use_voiceclone)
        )
        //【可选配置】在开启前述使用复刻音色的开关后，制定复刻音色所用的后端集群
        mSpeechEngine!!.setOptionString(
            SpeechEngineDefines.PARAMS_KEY_TTS_BACKEND_CLUSTER_STRING,
            mSettings!!.getString(R.string.config_backend_cluster)
        )

        // ------------------------ 离线合成相关配置 -----------------------
        var curVoiceOffline = mSettings!!.getString(R.string.config_voice_offline)
        if (curVoiceOffline.isEmpty()) {
            curVoiceOffline = mSettings!!.getOptionsValue(R.string.config_voice_offline)
        }
        mCurVoiceOffline = curVoiceOffline
        Log.d(TAG, "Current offline voice: $mCurVoiceOffline")
        //【必需配置】离线合成使用的发音人代号
        mSpeechEngine!!.setOptionString(
            SpeechEngineDefines.PARAMS_KEY_TTS_VOICE_OFFLINE_STRING,
            mCurVoiceOffline
        )
        var curVoiceTypeOffline = mSettings!!.getString(R.string.config_voice_type_offline)
        if (curVoiceTypeOffline.isEmpty()) {
            curVoiceTypeOffline = mSettings!!.getOptionsValue(R.string.config_voice_type_offline)
        }
        mCurVoiceTypeOffline = curVoiceTypeOffline
        Log.d(TAG, "Current offline voice type: $mCurVoiceTypeOffline")
        //【必需配置】离线合成使用的音色代号
        mSpeechEngine!!.setOptionString(
            SpeechEngineDefines.PARAMS_KEY_TTS_VOICE_TYPE_OFFLINE_STRING,
            mCurVoiceTypeOffline
        )

        //【可选配置】是否降低离线合成的 CPU 利用率，默认关闭
        // 打开该配置会使离线合成的实时率变大，仅当必要（例如为避免系统主动杀死CPU占用持续过高的进程）时才应开启
        mSpeechEngine!!.setOptionBoolean(
            SpeechEngineDefines.PARAMS_KEY_TTS_LIMIT_CPU_USAGE_BOOL,
            mSettings!!.getBoolean(R.string.tts_limit_cpu_usage)
        )
    }

    private fun AcquireAudioFocus() {
        // 向系统请求 Audio Focus 并记录返回结果
        val res = mAudioManager!!.requestAudioFocus(
            mAFChangeListener, AudioManager.STREAM_MUSIC,
            AudioManager.AUDIOFOCUS_GAIN
        )
        if (res == AudioManager.AUDIOFOCUS_REQUEST_FAILED) {
            mPlaybackNowAuthorized = false
        } else if (res == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
            mPlaybackNowAuthorized = true
        }
    }
    private fun clearResultText() {

    }

    private fun stopEngineBtnClicked() {
        Log.i(TAG, "关闭引擎（异步）")
        Log.i(TAG, "Directive: DIRECTIVE_STOP_ENGINE")
        if (mEngineStarted) {
            mSpeechEngine!!.sendDirective(SpeechEngineDefines.DIRECTIVE_STOP_ENGINE, "")
        }
    }


    private fun setResultText(text: String) {
    }

    fun createConnection() {
        if (mConnectionCreated) {
            Log.i(TAG, "Connection is created.")
            return
        }

        // DIRECTIVE_CREATE_CONNECTION 指令，可减小在线合成的端到端播放延时，主要应用在能够提前预知要使用语音合成的情况下，例如语音交互场景
        // DIRECTIVE_CREATE_CONNECTION 指令是一个同步指令，调用返回之后可以根据返回值判断连接是否建立成功
        // 如果不使用 DIRECTIVE_CREATE_CONNECTION 指令，建连实际发生在调用 DIRECTIVE_START_ENGINE 后
        Log.i(TAG, "触发提前建连")
        Log.i(TAG, "Directive: DIRECTIVE_CREATE_CONNECTION")
        val ret = mSpeechEngine!!.sendDirective(SpeechEngineDefines.DIRECTIVE_CREATE_CONNECTION, "")
        if (ret != SpeechEngineDefines.ERR_NO_ERROR) {
            val errorMessage = "在线合成提前建连失败: $ret"
            Log.e(TAG, errorMessage)
            createConnectionFailed(errorMessage)
        } else {
            val message = "在线合成提前建连成功 $ret"
            Log.e(TAG, message)
            createConnectionSucceeded(message)
        }
    }

    private fun prepareMultipleVoiceResource() {
        val resourceManager = SpeechResourceManagerGenerator.getInstance()
        Log.i(TAG, "初始化模型资源管理器")
        resourceManager.initResourceManager(
            context,
            "0",
            mCurAppId,
            SensitiveDefines.APP_VERSION,
            true,
            mDebugPath
        )
        // 因为多音色资源的一个文件包含了多个音色，导致资源的名字和音色的名字无法一一对应
        // 所以下载资源需要显式指定资源名字
        val resourceName = mSettings!!.getString(R.string.config_tts_model_name)
        Log.i(TAG, "检查本地是否存在可用资源")
        if (!resourceManager.checkResourceDownload(resourceName)) {
            Log.i(TAG, "本地没有资源，开始下载")
            fetchMultipleVoiceResource(resourceName)
        } else {
            Log.i(TAG, "资源存在，检查资源是否需要升级")
            resourceManager.checkResourceUpdate(
                resourceName
            ) { needUpdate ->
                if (needUpdate) {
                    Log.i(TAG, "存在可用升级，开始升级")
                    fetchMultipleVoiceResource(resourceName)
                } else {
                    Log.i(TAG, "不存在可用升级，使用本地已有模型")
                    initEngineInternal()
                }
            }
        }
    }

    private fun prepareSingleVoiceResource() {
        val resourceManager = SpeechResourceManagerGenerator.getInstance()
        resourceManager.setAppVersion(SensitiveDefines.APP_VERSION)
        resourceManager.setAppId(mCurAppId)
        resourceManager.setDeviceId("0")
        resourceManager.setUseOnlineModel(true)
        resourceManager.setEngineName(SpeechEngineDefines.TTS_ENGINE)
        Log.i(TAG, "初始化模型资源管理器")
        resourceManager.initResourceManager(context, mDebugPath)
        var needDownloadVoiceType = SensitiveDefines.TTS_DEFAULT_DOWNLOAD_OFFLINE_VOICES
        val voiceTypeArray = mSettings!!.getOptions(R.string.config_voice_type_offline).arrayObj
        if (voiceTypeArray != null && !voiceTypeArray.isEmpty()) {
            needDownloadVoiceType = voiceTypeArray.toTypedArray<String>()
        }
        Log.d(
            TAG,
            "离线合成将会使用的音色有： " + Arrays.toString(needDownloadVoiceType)
        )
        resourceManager.setTtsVoiceType(needDownloadVoiceType)
        var offlineLanguage = mSettings!!.getString(R.string.config_tts_language_offline)
        if (offlineLanguage.isEmpty()) {
            offlineLanguage = SensitiveDefines.TTS_DEFAULT_OFFLINE_LANGUAGE
        }
        val needDownloadLanauges = arrayOf(offlineLanguage)
        Log.d(TAG, "需要下载的离线合成语种资源有： $offlineLanguage")
        resourceManager.setTtsLanguage(arrayOf(offlineLanguage))
        Log.i(TAG, "检查本地是否存在可用资源")
        if (!resourceManager.checkResourceDownload()) {
            Log.i(TAG, "本地没有资源，开始下载")
            fetchSingleVoiceResource()
        } else {
            Log.i(TAG, "资源存在，检查资源是否需要升级")
            resourceManager.checkResourceUpdate { needUpdate ->
                if (needUpdate) {
                    Log.i(TAG, "存在可用升级，开始升级")
                    fetchSingleVoiceResource()
                } else {
                    Log.i(TAG, "不存在可用升级，使用本地已有模型")
                    initEngineInternal()
                }
            }
        }
    }
    private fun fetchSingleVoiceResource() {
        val resourceManager = SpeechResourceManagerGenerator.getInstance()
        resourceManager.fetchResource(object : FetchResourceListener {
            override fun onSuccess() {
                Log.i(TAG, "资源下载成功")
                initEngineInternal()
            }

            override fun onFailed(errorMsg: String) {
                Log.i(TAG, "资源下载失败，错误：$errorMsg")
                speechEngineInitFailed("Download tts resource failed.")
            }
        })
    }

    private fun fetchMultipleVoiceResource(resourceName: String) {
        Log.i(TAG, "需要下载的资源名为: $resourceName")
        val resourceManager = SpeechResourceManagerGenerator.getInstance()
        resourceManager.fetchResourceByName(resourceName,
            object : FetchResourceListener {
                override fun onSuccess() {
                    Log.i(TAG, "资源下载成功")
                    initEngineInternal()
                }

                override fun onFailed(errorMsg: String) {
                    Log.i(TAG, "资源下载失败，错误：$errorMsg")
                    speechEngineInitFailed("Download tts resource failed.")
                }
            })
    }



    private fun configInitParams() {
        //【必需配置】Engine Name
        mSpeechEngine!!.setOptionString(
            SpeechEngineDefines.PARAMS_KEY_ENGINE_NAME_STRING,
            SpeechEngineDefines.TTS_ENGINE
        )

        //【必需配置】Work Mode, 可选值如下
        // SpeechEngineDefines.TTS_WORK_MODE_ONLINE, 只进行在线合成，不需要配置离线合成相关参数；
        // SpeechEngineDefines.TTS_WORK_MODE_OFFLINE, 只进行离线合成，不需要配置在线合成相关参数；
        // SpeechEngineDefines.TTS_WORK_MODE_BOTH, 同时发起在线合成与离线合成，在线请求失败的情况下，使用离线合成数据，该模式会消耗更多系统性能；
        // SpeechEngineDefines.TTS_WORK_MODE_ALTERNATE, 先发起在线合成，失败后（网络超时），启动离线合成引擎开始合成；
        mSpeechEngine!!.setOptionInt(
            SpeechEngineDefines.PARAMS_KEY_TTS_WORK_MODE_INT,
            mCurTtsWorkMode
        )

        //【可选配置】Debug & Log
        mSpeechEngine!!.setOptionString(
            SpeechEngineDefines.PARAMS_KEY_DEBUG_PATH_STRING,
            mDebugPath
        )
        mSpeechEngine!!.setOptionString(
            SpeechEngineDefines.PARAMS_KEY_LOG_LEVEL_STRING,
            SpeechEngineDefines.LOG_LEVEL_DEBUG
        )

        //【可选配置】User ID（用以辅助定位线上用户问题）
        mSpeechEngine!!.setOptionString(
            SpeechEngineDefines.PARAMS_KEY_UID_STRING,
            SensitiveDefines.UID
        )
        mSpeechEngine!!.setOptionString(
            SpeechEngineDefines.PARAMS_KEY_DEVICE_ID_STRING,
            SensitiveDefines.DID
        )

        //【可选配置】是否将合成出的音频保存到设备上，为 true 时需要正确配置 PARAMS_KEY_TTS_AUDIO_PATH_STRING 才会生效
        mSpeechEngine!!.setOptionBoolean(
            SpeechEngineDefines.PARAMS_KEY_TTS_ENABLE_DUMP_BOOL,
            mSettings!!.getBoolean(R.string.config_tts_dump)
        )
        // TTS 音频文件保存目录，必须在合成之前创建好且 APP 具有访问权限，保存的音频文件名格式为 tts_{reqid}.wav, {reqid} 是本次合成的请求 id
        // PARAMS_KEY_TTS_ENABLE_DUMP_BOOL 配置为 true 的音频时为【必需配置】，否则为【可选配置】
        mSpeechEngine!!.setOptionString(
            SpeechEngineDefines.PARAMS_KEY_TTS_AUDIO_PATH_STRING,
            mDebugPath
        )

        //【可选配置】合成出的音频的采样率，默认为 24000
        mSpeechEngine!!.setOptionInt(
            SpeechEngineDefines.PARAMS_KEY_TTS_SAMPLE_RATE_INT,
            mSettings!!.getInt(R.string.config_tts_sample_rate)
        )
        //【可选配置】打断播放时使用多长时间淡出停止，单位：毫秒。默认值 0 表示不淡出
        mSpeechEngine!!.setOptionInt(
            SpeechEngineDefines.PARAMS_KEY_AUDIO_FADEOUT_DURATION_INT,
            mSettings!!.getInt(R.string.config_audio_fadeout_duration)
        )

        // ------------------------ 在线合成相关配置 -----------------------
        mCurAppId = mSettings!!.getString(R.string.config_app_id)
        if (mCurAppId.isEmpty()) {
            mCurAppId = SensitiveDefines.APPID
        }
        Log.e(TAG, "mCurAppId: $mCurAppId")
        //【必需配置】在线合成鉴权相关：Appid
        mSpeechEngine!!.setOptionString(SpeechEngineDefines.PARAMS_KEY_APP_ID_STRING, mCurAppId)
        var token = mSettings!!.getString(R.string.config_token)
        if (token.isEmpty()) {
            token = SensitiveDefines.TOKEN
        }
        //        token = "kL3uotMJx0VsDcJuAoTVJ5B4zoAK8jKo";
        //【必需配置】在线合成鉴权相关：Token
        mSpeechEngine!!.setOptionString(SpeechEngineDefines.PARAMS_KEY_APP_TOKEN_STRING, token)
        Log.e(TAG, "token: $token")
        var address = mSettings!!.getString(R.string.config_address)
        if (address.isEmpty()) {
            address = SensitiveDefines.DEFAULT_ADDRESS
        }
        Log.e(TAG, "Current address: $address")
        //【必需配置】语音合成服务域名
        mSpeechEngine!!.setOptionString(SpeechEngineDefines.PARAMS_KEY_TTS_ADDRESS_STRING, address)
        var uri = mSettings!!.getString(R.string.config_uri)
        if (uri.isEmpty()) {
            uri = SensitiveDefines.TTS_DEFAULT_URI
        }
        Log.i(TAG, "Current uri: $uri")
        //【必需配置】语音合成服务Uri
        mSpeechEngine!!.setOptionString(SpeechEngineDefines.PARAMS_KEY_TTS_URI_STRING, uri)
        var cluster = mSettings!!.getString(R.string.config_cluster)
        if (cluster.isEmpty()) {
            cluster = SensitiveDefines.TTS_DEFAULT_CLUSTER
        }
        Log.e(TAG, "Current cluster: $cluster")
        //【必需配置】语音合成服务所用集群
        mSpeechEngine!!.setOptionString(SpeechEngineDefines.PARAMS_KEY_TTS_CLUSTER_STRING, cluster)

        //【可选配置】在线合成下发的 opus-ogg 音频的压缩倍率
        mSpeechEngine!!.setOptionInt(SpeechEngineDefines.PARAMS_KEY_TTS_COMPRESSION_RATE_INT, 10)

        // ------------------------ 离线合成相关配置 -----------------------
        if (mCurTtsWorkMode != SpeechEngineDefines.TTS_WORK_MODE_ONLINE && mCurTtsWorkMode != SpeechEngineDefines.TTS_WORK_MODE_FILE) {
            var ttsResourcePath = ""
            val resourceManager = SpeechResourceManagerGenerator.getInstance()
            if (mSettings!!.getOptionsValue(
                    R.string.tts_offline_resource_format_title,
                    context
                ) == "MultipleVoice"
            ) {
                ttsResourcePath =
                    resourceManager.getResourcePath(mSettings!!.getString(R.string.config_tts_model_name))
            } else if (mSettings!!.getOptionsValue(
                    R.string.tts_offline_resource_format_title,
                    context
                ) == "SingleVoice"
            ) {
                ttsResourcePath = resourceManager.resourcePath
            }
            Log.d(TAG, "tts resource root path:$ttsResourcePath")
            //【必需配置】离线合成所需资源存放路径
            mSpeechEngine!!.setOptionString(
                SpeechEngineDefines.PARAMS_KEY_TTS_OFFLINE_RESOURCE_PATH_STRING,
                ttsResourcePath
            )
        }

        //【必需配置】离线合成鉴权相关：证书文件存放路径
        mSpeechEngine!!.setOptionString(
            SpeechEngineDefines.PARAMS_KEY_LICENSE_DIRECTORY_STRING,
            mDebugPath
        )
        val curAuthenticateType = mAuthenticationTypeArray[mSettings!!
            .getOptions(R.string.config_authenticate_type).chooseIdx]
        //【必需配置】Authenticate Type
        mSpeechEngine!!.setOptionString(
            SpeechEngineDefines.PARAMS_KEY_AUTHENTICATE_TYPE_STRING,
            curAuthenticateType
        )
        if (curAuthenticateType == SpeechEngineDefines.AUTHENTICATE_TYPE_PRE_BIND) {
            // 按包名授权，获取到授权的 APP 可以不限次数、不限设备数的使用离线合成
            val ttsLicenseName = mSettings!!.getString(R.string.config_license_name)
            val ttsLicenseBusiId = mSettings!!.getString(R.string.config_license_busi_id)

            // 证书名和业务 ID, 离线合成鉴权相关，使用火山提供的证书下发服务时为【必需配置】, 否则为【无需配置】
            // 证书名，用于下载按报名授权的证书文件
            mSpeechEngine!!.setOptionString(
                SpeechEngineDefines.PARAMS_KEY_LICENSE_NAME_STRING,
                ttsLicenseName
            )
            // 业务 ID, 用于下载按报名授权的证书文件
            mSpeechEngine!!.setOptionString(
                SpeechEngineDefines.PARAMS_KEY_LICENSE_BUSI_ID_STRING,
                ttsLicenseBusiId
            )
        } else if (curAuthenticateType == SpeechEngineDefines.AUTHENTICATE_TYPE_LATE_BIND) {
            // 按装机量授权，不限制 APP 的包名和使用次数，但是限制使用离线合成的设备数量
            //【必需配置】离线合成鉴权相关：Authenticate Address
            mSpeechEngine!!.setOptionString(
                SpeechEngineDefines.PARAMS_KEY_AUTHENTICATE_ADDRESS_STRING,
                SensitiveDefines.AUTHENTICATE_ADDRESS
            )
            //【必需配置】离线合成鉴权相关：Authenticate Uri
            mSpeechEngine!!.setOptionString(
                SpeechEngineDefines.PARAMS_KEY_AUTHENTICATE_URI_STRING,
                SensitiveDefines.AUTHENTICATE_URI
            )
            val businessKey = mSettings!!.getString(R.string.config_business_key)
            val authenticateSecret = mSettings!!.getString(R.string.config_authenticate_secret)
            //【必需配置】离线合成鉴权相关：Business Key
            mSpeechEngine!!.setOptionString(
                SpeechEngineDefines.PARAMS_KEY_BUSINESS_KEY_STRING,
                businessKey
            )
            //【必需配置】离线合成鉴权相关：Authenticate Secret
            mSpeechEngine!!.setOptionString(
                SpeechEngineDefines.PARAMS_KEY_AUTHENTICATE_SECRET_STRING,
                authenticateSecret
            )
        }

        // ------------------------ 在离线切换相关配置 -----------------------
        if (mCurTtsWorkMode == SpeechEngineDefines.TTS_WORK_MODE_ALTERNATE) {
            // 断点续播功能在断点处会发生由在线合成音频切换到离线合成音频，为了提升用户体验，SDK 支持
            // 淡出地停止播放在线音频然后再淡入地开始播放离线音频，下面两个参数可以控制淡出淡入的长度

            //【可选配置】断点续播专用，切换到离线合成时淡入的音频长度，单位：毫秒
            mSpeechEngine!!.setOptionInt(SpeechEngineDefines.PARAMS_KEY_TTS_FADEIN_DURATION_INT, 30)
            //【可选配置】断点续播专用，在线合成停止播放时淡出的音频长度，单位：毫秒
            mSpeechEngine!!.setOptionInt(
                SpeechEngineDefines.PARAMS_KEY_TTS_FADEOUT_DURATION_INT,
                30
            )
        }
    }

    override fun onSpeechMessage(type: Int, data: ByteArray?, len: Int) {
        var stdData = ""
        stdData = String(data!!)
        when (type) {
            SpeechEngineDefines.MESSAGE_TYPE_ENGINE_START -> {
                // Callback: 引擎启动成功回调
                Log.i(TAG, "Callback: 引擎启动成功: data: $stdData")
                speechStart(stdData)
            }

            SpeechEngineDefines.MESSAGE_TYPE_ENGINE_STOP -> {
                // Callback: 引擎关闭回调
                Log.i(TAG, "Callback: 引擎关闭: data: $stdData")
                speechStop(stdData)
            }

            SpeechEngineDefines.MESSAGE_TYPE_ENGINE_ERROR -> {
                // Callback: 错误信息回调
                Log.e(TAG, "Callback: 错误信息: $stdData")
                speechError(stdData)
            }

            SpeechEngineDefines.MESSAGE_TYPE_TTS_SYNTHESIS_BEGIN -> {
                // Callback: 合成开始回调
                Log.e(TAG, "Callback: 合成开始: $stdData")
                speechStartSynthesis(stdData)
            }

            SpeechEngineDefines.MESSAGE_TYPE_TTS_SYNTHESIS_END -> {
                // Callback: 合成结束回调
                Log.e(TAG, "Callback: 合成结束: $stdData")
                speechFinishSynthesis(stdData)
            }

            SpeechEngineDefines.MESSAGE_TYPE_TTS_START_PLAYING -> {
                // Callback: 播放开始回调
                Log.e(TAG, "Callback: 播放开始: $stdData")
                speechStartPlaying(stdData)
            }

            SpeechEngineDefines.MESSAGE_TYPE_TTS_PLAYBACK_PROGRESS -> {
                // Callback: 播放进度回调
                Log.e(TAG, "Callback: 播放进度")
                speechPlayingProgress(stdData)
            }

            SpeechEngineDefines.MESSAGE_TYPE_TTS_FINISH_PLAYING -> {
                // Callback: 播放结束回调
                Log.e(TAG, "Callback: 播放结束: $stdData")
                speechFinishPlaying(stdData)
            }

            SpeechEngineDefines.MESSAGE_TYPE_TTS_AUDIO_DATA -> {
                // Callback: 音频数据回调
                Log.e(
                    TAG,
                    String.format("Callback: 音频数据，长度 %d 字节", stdData.length)
                )
                speechTtsAudioData(stdData)
            }

            else -> {}
        }
    }

    private fun speechEnginInitSucceeded(initCost: Long) {
        Log.i(TAG, "引擎初始化成功!")
        mEngineInited = true
    }

    private fun speechEngineInitFailed(tipText: String) {
        Log.e(TAG, "引擎初始化失败: $tipText")
        mEngineInited = false
    }

    private fun createConnectionSucceeded(tipText: String) {
        Log.e(TAG, "在线合成提前建连成功: $tipText")
        mConnectionCreated = true
    }

    private fun createConnectionFailed(tipText: String) {
        Log.e(TAG, "在线合成提前建连失败: $tipText")
        mConnectionCreated = false
    }

    private fun speechStart(data: String) {
        mEngineStarted = true
    }

    private fun speechStop(data: String) {
        mEngineStarted = false
        mConnectionCreated = false
        mPlayerPaused = false

        // Abandon audio focus when playback complete
        if (mPlaybackNowAuthorized) {
            mAudioManager!!.abandonAudioFocus(mAFChangeListener)
            mPlaybackNowAuthorized = false
        }
    }

    private fun speechError(data: String) {
         val reader = JSONObject(data)
         if (!reader.has("err_code") || !reader.has("err_msg")) {
             return
         }
         setResultText(data)
    }

    private fun speechStartSynthesis(data: String) {}

    private fun speechFinishSynthesis(data: String) {}

    private fun speechStartPlaying(data: String) {
    }

    private fun speechPlayingProgress(data: String) {
        try {
            val reader = JSONObject(data)
            if (!reader.has("reqid") || !reader.has("progress")) {
                Log.w(TAG, "Can't find necessary field in progress callback. ")
                return
            }
            val percentage = reader.getDouble("progress")
            val reqid = reader.getString("reqid")
            Log.d(
                TAG,
                "当前播放的文本对应的 reqid: $reqid, 播放进度：$percentage"
            )
        } catch (e: JSONException) {
            e.printStackTrace()
        }
    }

    private fun speechFinishPlaying(data: String) {}

    private fun speechTtsAudioData(data: String) {}

}